#include <cxcore.h>
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <math.h>
//#include <direct.h>
#include <iostream>

//the manual
#include "faceson.h"

using namespace std;

bool runface(IplImage *res){
    bool don1;
    IplImage* mo = cvCreateImage(cvSize(res->width,res->height), IPL_DEPTH_8U, 1);
    IplImage* ycb = cvCreateImage(cvSize(res->width,res->height), IPL_DEPTH_8U, 3);
    IplImage* gr = cvCreateImage(cvSize(res->width,res->height), IPL_DEPTH_8U, 1);
    res=Lmain(res);
 // cvNamedWindow("result", CV_WINDOW_AUTOSIZE);  
    cvCvtColor(res,ycb,CV_BGR2YCrCb);
    mo=Mmain(ycb);    
    gr=Smain(res);
//    cvShowImage( "result",res);
//    cvWaitKey( 20000 );
    don1=Kmain(res,gr,mo);
    
    cvReleaseImage( &gr );
    cvReleaseImage( &mo );
    cvReleaseImage( &ycb );
    return don1;

}



IplImage * Lmain(IplImage * resim){
    CvScalar s;
  
    int i,j;
    
//   resim = cvLoadImage("resim27.jpg",1); 
    
   IplImage* ycb = cvCreateImage(cvSize(resim->width,resim->height), IPL_DEPTH_8U, 3);
   IplImage* yeni = cvCreateImage(cvSize(resim->width,resim->height), IPL_DEPTH_8U, 3);
    if( !resim )
        cout << "Resim acmada sorun!";

    int max_y=0,yt;
    int sum = 0, sum_r = 0, sum_g = 0, sum_b = 0, r, g, b;
    cvCvtColor(resim,ycb,CV_BGR2YCrCb);
    for(i=0;i<(ycb->height)-1;i++){
      for(j=0;j<(ycb->width)-1;j++){
         s=cvGet2D(ycb,i,j);
         if (s.val[0] > max_y) max_y =(int)s.val[0];
           }
        }
        
        yt = static_cast<int> (0.95 * (double)max_y);
        
         
        
   for(i=0;i<(resim->height)-1;i++){
      for(j=0;j<(resim->width)-1;j++){
         if ((int)((uchar *)(ycb->imageData + i*ycb->widthStep))[j*ycb->nChannels + 0] >= yt){
   			sum_r +=(int)((uchar *)(resim->imageData + i*resim->widthStep))[j*resim->nChannels + 2];
			sum_g +=(int)((uchar *)(resim->imageData + i*resim->widthStep))[j*resim->nChannels + 1];
			sum_b +=(int)((uchar *)(resim->imageData + i*resim->widthStep))[j*resim->nChannels + 0];
			sum++;
            }
           }
        }
   for(i=0;i<(resim->height)-1;i++){
      for(j=0;j<(resim->width)-1;j++){
		if (sum >= 100 && sum_r!=0 && sum_g!=0 && sum_b!=0) {
			r = (sum * 255 * (int)((uchar *)(resim->imageData + i*resim->widthStep))[j*resim->nChannels + 2]) / sum_r;
			g = (sum * 255 * (int)((uchar *)(resim->imageData + i*resim->widthStep))[j*resim->nChannels + 1]) / sum_g;
			b = (sum * 255 * (int)((uchar *)(resim->imageData + i*resim->widthStep))[j*resim->nChannels + 0]) / sum_b;
			((uchar *)(yeni->imageData + i*yeni->widthStep))[j*yeni->nChannels + 2] = (r > 255) ? 255 : r;
			((uchar *)(yeni->imageData + i*yeni->widthStep))[j*yeni->nChannels + 1] = (g > 255) ? 255 : g;
			((uchar *)(yeni->imageData + i*yeni->widthStep))[j*yeni->nChannels + 0] = (b > 255) ? 255 : b;
		}else{
	((uchar *)(yeni->imageData + i*yeni->widthStep))[j*yeni->nChannels + 0]=((uchar *)(resim->imageData + i*resim->widthStep))[j*resim->nChannels + 0];
    ((uchar *)(yeni->imageData + i*yeni->widthStep))[j*yeni->nChannels + 1]=((uchar *)(resim->imageData + i*resim->widthStep))[j*resim->nChannels + 1]; 
    ((uchar *)(yeni->imageData + i*yeni->widthStep))[j*yeni->nChannels + 2]=((uchar *)(resim->imageData + i*resim->widthStep))[j*resim->nChannels + 2];
        }
           }
        }
       
    cvReleaseImage( &ycb );
      
    return yeni;

        
 }
//*********************************************************************************
IplImage * Smain(IplImage * resim){
         
    CvScalar s,s1;
    int i,j,Y,Cb,Cr;
    IplImage* ycb = cvCreateImage(cvSize(resim->width,resim->height), IPL_DEPTH_8U, 3);
    IplImage* skin = cvCreateImage(cvSize(resim->width,resim->height), IPL_DEPTH_8U, 3);
    IplImage* mask = cvCreateImage(cvSize(resim->width,resim->height), IPL_DEPTH_8U, 1);
    cvCvtColor(resim,ycb,CV_BGR2YCrCb);

    CvMat *cb = cvCreateMat( resim->height, resim->width, CV_32FC1);
    CvMat *cr = cvCreateMat( resim->height, resim->width, CV_32FC1);
    
    const unsigned char Kl = 125;
    const unsigned char Kh = 188;
    const float Wcb = 46.97;
    const unsigned char WLcb = 23;
    const unsigned char WHcb = 14;
    const float Wcr = 38.76;
    const unsigned char WLcr = 20;
    const unsigned char WHcr = 10;
    const unsigned char Ymin = 16;
    const unsigned char Ymax = 235;    
    float CbP, CrP,WcbY,WcrY,CbBarY,CrBarY;
    float a2=644.6521;
    float b2=196.8409,x,y;
   for(i=0;i<(ycb->height)-1;i++){
      for(j=0;j<(ycb->width)-1;j++){
         s=cvGet2D(ycb,i,j);
         Y=(int)s.val[0];
         Cr=(int)s.val[1];
         Cb=(int)s.val[2];
         if (Y >= Kl && Y <= Kh){
          CbP = Cb;
          CrP = Cr;
         }else{
             if (Y < Kl){
              WcbY = WLcb + ((Y - Ymin) * (Wcb - WLcb)) / (Kl - Ymin);
              WcrY = WLcr + ((Y - Ymin) * (Wcr - WLcr)) / (Kl - Ymin);
    
              CbBarY = 108 + ((Kl - Y) * (118 - 108)) / (Kl - Ymin);
              CrBarY = 154 - ((Kl - Y) * (154 - 144)) / (Kl - Ymin);
             }else{ 
              WcbY = WHcb + ((Ymax - Y) * (Wcb - WHcb)) / (Ymax - Kh);
              WcrY = WHcr + ((Ymax - Y) * (Wcr - WHcr)) / (Ymax - Kh);

              CbBarY = 108 + ((Y - Kh) * (118 - 108)) / (Ymax - Kh);
              CrBarY = 154 - ((Y - Kh) * (154 - 132)) / (Ymax - Kh);
             }
              CbP = (Cb - CbBarY) * (Wcb / WcbY) + CbBarY;
              CrP = (Cr - CrBarY) * (Wcr / WcrY) + CrBarY;
          }
         
    //      cvmSet(cb,i,j,CbP);
    //      cvmSet(cr,i,j,CrP);
          s.val[2]=(CbP < 0) ? 0 : ((CbP > 255) ? 255: (unsigned char) (CbP));
          s.val[1]=(CrP < 0) ? 0 : ((CrP > 255) ? 255: (unsigned char) (CrP));
          CbP=(float)s.val[2];
          CrP=(float)s.val[1];
          
          x=(cos(2.53)*(CbP-109.38)+ sin(2.53)*(CrP-152.02));
          y=((-1)*sin(2.53)*(CbP-109.38)+ cos(2.53)*(CrP-152.02));
          //printf("%d %d\n",(int)CbP,(int)CrP);
         // fprintf(fp,"%f %f\n",x,y);
          if(((pow((x-1.6),2)/a2)+(pow((y-2.41),2)/b2))<1.0){
          s1.val[0]=255;                                               
          }else{
          s1.val[0]=0;
          } 

          cvSet2D(skin,i,j,s);
          cvSet2D(mask,i,j,s1);
          
          
       }
    }

    cvReleaseMat(&cb);
    cvReleaseMat(&cr);

    cvReleaseImage( &skin );
    cvReleaseImage( &ycb);
    
    return mask;

   
}
//*******************************************************************************
IplImage * Mmain(IplImage * m){
         
          CvScalar cm,cm2;
          int i,j;
          double scr=0.0,scrcb=0.0,nu,n,cr,crb;
          CvMat *mou = cvCreateMat( m->height, m->width, CV_32FC1);
          CvMat *cr2 = cvCreateMat( m->height, m->width, CV_32FC1);
          CvMat *crcb = cvCreateMat( m->height, m->width, CV_32FC1);
          CvMat *cr21 = cvCreateMat( m->height, m->width, CV_32FC1);
          CvMat *crcb1 = cvCreateMat( m->height, m->width, CV_32FC1);
          IplImage* son = cvCreateImage(cvSize(m->width,m->height), IPL_DEPTH_8U, 1);
          IplImage* dilson = cvCreateImage(cvSize(m->width,m->height), IPL_DEPTH_8U, 1);
    for(i=0;i<(m->height)-1;i++){
      for(j=0;j<(m->width)-1;j++){
          cm=cvGet2D(m,i,j);                       
          cvmSet(cr2,i,j,pow((double)cm.val[1],2));                          
          cvmSet(crcb,i,j,(double)(cm.val[1]/cm.val[2]));
      }
    }      
    cvNormalize(cr2,cr21,0,255, CV_MINMAX, 0);
    cvNormalize(crcb,crcb1,0,255, CV_MINMAX, 0);
          
    for(i=0;i<(m->height)-1;i++){
      for(j=0;j<(m->width)-1;j++){
          scr+=cvmGet(cr21,i,j);
          scrcb+=cvmGet(crcb1,i,j);        
      }
    }          
    n=m->height*m->width;
    nu=0.95*((scr/n)/(scrcb/n));
    
    for(i=0;i<(m->height)-1;i++){
      for(j=0;j<(m->width)-1;j++){
       cr=cvmGet(cr21,i,j);
       crb=cvmGet(crcb1,i,j);                 
       cvmSet(mou,i,j,cr*pow((cr-(nu*crb)),2));                          
      }
    }                    
    cvNormalize(mou,mou,0,255, CV_MINMAX, 0);            
    for(i=0;i<(m->height)-1;i++){
      for(j=0;j<(m->width)-1;j++){
       cm2.val[0]= cvmGet(mou,i,j);   
       cvSet2D(son,i,j,cm2);                      
      }
    }
   cvDilate(son,dilson,NULL,1);
   cvThreshold(dilson, son, 150, 255, CV_THRESH_BINARY);
  //  cvDilate(son,son,NULL,1);
    cvReleaseMat(&mou);
    cvReleaseMat(&cr2);
    cvReleaseMat(&crcb);  
    cvReleaseMat(&cr21);
    cvReleaseMat(&crcb1);
    cvReleaseImage(&dilson);  
    return son;

  
}
//***************************************************************************
bool Kmain(IplImage* resim,IplImage* gr,IplImage* mo){
   bool don;
   int w=0,h=0;
   CvRect bndRect = cvRect(0,0,0,0);
   CvRect mRect = cvRect(0,0,0,0);
   CvMemStorage* storage = cvCreateMemStorage(0);
   CvSeq* contour = 0;
   CvSeq* contour1 = 0;
   cvFindContours( gr, storage, &contour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
   cvFindContours( mo, storage, &contour1, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
   CvPoint pt1, pt2;
   CvPoint ma1, ma2;
      CvPoint ptm1, ptm2;
   CvPoint mam1, mam2;
   ma1.x=1;
   ma2.x=1;
   ma1.y=1;
   ma2.y=1;
      mam1.x=1;
   mam2.x=1;
   mam1.y=1;
   mam2.y=1;
   for( ; contour1 != 0; contour1 = contour1->h_next ){
   mRect = cvBoundingRect(contour1, 0);
   ptm1.x = mRect.x;
   ptm1.y = mRect.y;
   ptm2.x = mRect.x + mRect.width;
   ptm2.y = mRect.y + mRect.height;
   if(abs(ptm1.x-ptm2.x)>abs(mam1.x-mam2.x)){
   mam1.x=ptm1.x;
   mam2.x=ptm2.x;
   w=mRect.width;
    // if(abs(ptm1.y-ptm2.y)>abs(mam1.y-mam2.y)){
      mam1.y=ptm1.y;
      mam2.y=ptm2.y;
      h=mRect.height;
    // }
   }
//cvRectangle(resim, ptm1, ptm2, CV_RGB(0,255,0), 1);
}
 for( ; contour != 0; contour = contour->h_next ){
   bndRect = cvBoundingRect(contour, 0);
   pt1.x = bndRect.x;
   pt1.y = bndRect.y;
   pt2.x = bndRect.x + bndRect.width;
   pt2.y = bndRect.y + bndRect.height;
   
   if((((mam1.x>=(pt1.x-w/2) && mam1.x<=(pt2.x+w/2)) || (mam2.x<=(pt2.x+w/2) && mam2.x>=(pt1.x+w/2))) && (mam1.y>=(pt1.y-h/2) && mam2.y<=(pt2.y+h/2) )) && abs(pt1.x-pt2.x)>abs(ma1.x-ma2.x)){
   ma1.x=pt1.x;
   ma2.x=pt2.x;
      //   if((mam1.y>=(pt1.y-h/2) || mam2.y<=(pt2.y+h/2)) && abs(pt1.y-pt2.y)>abs(ma1.y-ma2.y)){
            ma1.y=pt1.y;
            ma2.y=pt2.y;
        // }
   }

//cvRectangle(resim, pt1, pt2, CV_RGB(255,0,0), 1);
}  

   IplImage* crop = cvCreateImage(cvSize(abs(ma2.x-ma1.x),abs(ma2.y-ma1.y)), IPL_DEPTH_8U, 3);
   IplImage* gri = cvCreateImage(cvSize(92,112), IPL_DEPTH_8U, 1);
   IplImage* boyut = cvCreateImage(cvSize(92,112), IPL_DEPTH_8U, 3);
   
   if(abs(ma2.x-ma1.x)!=0 && abs(ma2.y-ma1.y)!=0){
   IplImage* ye;
   mRect = cvRect(ma1.x,ma1.y,abs(ma2.x-ma1.x),abs(ma2.y-ma1.y));
   cvSetImageROI(resim,mRect);
   cvCopy(resim,crop,0);
   
   ye=resz(crop);

   cvResize(ye,boyut,CV_INTER_LINEAR);

   cvCvtColor(boyut,gri,CV_BGR2GRAY);

//  
   cvEqualizeHist( gri, gri );
   //cvNamedWindow("result", CV_WINDOW_AUTOSIZE);  
   // cvShowImage( "result",gri);
   // cvWaitKey(10);
   cvSaveImage("tester/1_crop_1.pgm",gri);
   don=true;
   cvReleaseImage(&ye);
   }else{
   don=false;
   }
 //   cvDestroyWindow("result");
    cvReleaseImage(&crop);
    cvReleaseImage(&gri);
    cvReleaseImage(&boyut);
    cvFree(&storage);
    return don;

}
IplImage * resz(IplImage * kes){
         //*******************************************************************************************
    CvScalar s,s1;
    int i,j,gs,wk,hk;
    float w,h;
   CvMemStorage* storage = cvCreateMemStorage(0);
   CvSeq* contour = 0;
   GOZ g[5],l,r;
    
    IplImage* ycb = cvCreateImage(cvSize(kes->width,kes->height), IPL_DEPTH_8U, 3);
    IplImage* son2 = cvCreateImage(cvSize(kes->width,kes->height), IPL_DEPTH_8U, 1);
    IplImage* eyemapc = cvCreateImage(cvSize(kes->width,kes->height), IPL_DEPTH_8U, 1);
    IplImage* eyemapc1 = cvCreateImage(cvSize(kes->width,kes->height), IPL_DEPTH_8U, 1);

    cvCvtColor(kes,ycb,CV_BGR2YCrCb);    
   
    CvMat *cb = cvCreateMat( kes->height, kes->width, CV_32FC1);
    CvMat *cr = cvCreateMat( kes->height, kes->width, CV_32FC1);
    CvMat *cbcr = cvCreateMat( kes->height, kes->width, CV_32FC1);
    CvMat *gri = cvCreateMat( kes->height, kes->width, CV_32FC1);
                       

    for(i=0;i<(ycb->height);i++){
      for(j=0;j<(ycb->width);j++){
        s=cvGet2D(ycb,i,j);

        cvmSet(gri,i,j,s.val[0]);
        cvmSet(cb,i,j,pow((int)s.val[2],2));
        cvmSet(cr,i,j,pow((int)(255-s.val[1]),2));
        cvmSet(cbcr,i,j,(float)(s.val[2]/s.val[1]));
      }
    }
    
    cvNormalize(cb,cb,0,255, CV_MINMAX, 0);
    cvNormalize(cr,cr,0,255, CV_MINMAX, 0);
    cvNormalize(cbcr,cbcr,0,255, CV_MINMAX, 0);
    
    for(i=0;i<(ycb->height);i++){
      for(j=0;j<(ycb->width);j++){
      s1.val[0]=(cvmGet(cb,i,j)+cvmGet(cr,i,j)+cvmGet(cbcr,i,j))/3;
      cvSet2D(eyemapc,i,j,s1);
      }
    }
   cvEqualizeHist( eyemapc,eyemapc1 );
   
   CvRect bndRect = cvRect(0,0,0,0);
   CvPoint pt1, pt2,pt3,pt4;

   //cvSaveImage("eyemapc1.jpg",eyemapc);
   
   cvThreshold(eyemapc1, eyemapc, 150, 255, CV_THRESH_BINARY);
   cvDilate(eyemapc,eyemapc1,NULL,1);
   cvSaveImage("eyemapc.jpg",eyemapc1);
  // cvSaveImage("eyemapc.jpg",eyemapc);
   i=0;
   cvFindContours( eyemapc1, storage, &contour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
   for( ; contour != 0; contour = contour->h_next ){
   bndRect = cvBoundingRect(contour, 0);
   pt1.x = bndRect.x;
   pt1.y = bndRect.y;
   pt2.x = bndRect.x + bndRect.width;
   pt2.y = bndRect.y + bndRect.height;
   
   if(pt1.x>5 && pt2.x<kes->width-5 && pt1.y<kes->height/2 && pt1.y>10 && (bndRect.width*bndRect.height)>80){
    if(i<5){
      g[i].x=pt1.x;
      g[i].y=pt1.y;
      g[i].width=bndRect.width;
      g[i].height=bndRect.height;
     // printf("%d %d\n",g[i].width,g[i].height); 
      i++;
  //cvRectangle(kes, pt1, pt2, CV_RGB(0,255,0), 1);
    // printf("%d %d %d %d\n",pt1.x,pt1.y,pt2.x,pt2.y);
    }
   }
 }
//printf("kare sayisi:%d\n",i);
gs=i;
r.x=kes->width;
l.width=-1;
r.width=-1;
IplImage* dst;
  if(i==0){
 //  printf("Goz Yok!\n");
   dst=cvCreateImage(cvSize(kes->width,kes->height), IPL_DEPTH_8U, 3);
   dst=cvCloneImage(kes);
  }else{
 
     while(i>0){
       i=i-1;
       if(g[i].x<=kes->width/1.5 && g[i].x<r.x){
         r=g[i];
          if(gs==1){
            l.width=-1;
          }
       }
     }
      
      i=gs; 
      if(l.width!=-1){
       while(i>0){
        i=i-1;
       if(g[i].x>r.x && abs(r.y-g[i].y)<15 && (abs(r.x-g[i].x)<kes->width*0.6)){
         l=g[i];
         }
       }
      }
  
  if(l.width>kes->width){
  l.width=-1;

  }
  if(r.width>kes->width){
  r.width=-1;
  }
  
  if(l.width!=-1 && r.width!=-1){

  dst=cvCreateImage(cvSize((int)(kes->width*((float)20/abs(l.x-(r.x+r.width)))),(int)(kes->height*((float)20/abs(l.x-(r.x+r.width))))), IPL_DEPTH_8U, 3);
  
  cvResize(kes,dst);
  
  }else {
   dst=cvCloneImage(kes);
  
  }
    CvRect mRect;
  if(l.width!=-1 && r.width!=-1){
  w=(float)(l.x+l.width+40-(r.x-40))*((float)20/abs(l.x-(r.x+r.width)));
  h=(float)(r.y+90-(r.y-60))*((float)20/abs(l.x-(r.x+r.width)));
   //  printf("fark: %f %f %d %d\n",w,h,dst->width,dst->height);
  // if((int)w<=dst->width && (int)h<=dst->height){
     if(r.x-40<0){
     wk=0;
     }else{
     wk=r.x-40;
     }
     if(r.y-60<0){
     hk=0;
     }else{
     hk=r.y-60;
     }
  //  printf("mRect: %d %d %f %f\n",wk,hk,w,h); 
    if(wk+w<dst->width && hk+h<dst->height){
     mRect = cvRect(wk,hk,(int)w,(int)h);
     cvSetImageROI(dst,mRect);  
    }
  // }
  }
}

//  printf("right:%d %d\nleft:%d %d\n",r.width,r.height,l.width,l.height);
pt3.x=r.x;
pt3.y=r.y;
pt4.x=r.x+r.width;
pt4.y=r.y+r.height;
//cvRectangle(kes, pt3, pt4, CV_RGB(255,0,0), 1);
pt3.x=l.x;
pt3.y=l.y;
pt4.x=l.x+l.width;
pt4.y=l.y+l.height;
//cvRectangle(kes, pt3, pt4, CV_RGB(0,0,255), 1);

 cvReleaseImage(&ycb);
 cvFree(&storage);
 cvReleaseImage(&son2);
  cvReleaseImage(&eyemapc);
  cvReleaseImage(&eyemapc1);
  cvReleaseMat(&cb);
  cvReleaseMat(&cr);
  cvReleaseMat(&cbcr);
  cvReleaseMat(&gri);
return dst;
 
         //*******************************************************************************************
}
